/*
 * BaseTazioNetwork.java    0.0.1    27/dic/2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.transparency.impl;

import java.io.IOException;
import java.io.Serializable;
import java.util.List;

import org.mentalsmash.tazio.commons.identifiers.NID;
import org.mentalsmash.tazio.net.cs.CSSubsystemException;
import org.mentalsmash.tazio.net.cs.server.CSComFacade;
import org.mentalsmash.tazio.net.cs.server.Configuration;
import org.mentalsmash.tazio.transparency.commons.messages.TazioMessage;
import org.mentalsmash.tazio.transparency.interfaces.Dispatcher;
import org.mentalsmash.tazio.transparency.interfaces.GroupConnection;
import org.mentalsmash.tazio.transparency.interfaces.InputMessageListener;
import org.mentalsmash.tazio.transparency.interfaces.MessageFilter;
import org.mentalsmash.tazio.transparency.interfaces.Point2PointCommunication;
import org.mentalsmash.tazio.transparency.interfaces.Resolver;
import org.mentalsmash.tazio.transparency.interfaces.TazioNetwork;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.google.inject.name.Named;

/**
 * This is the main implementation for the {@link TazioNetwork} implementation.
 * 
 * @version 0.0.1 27/dic/2009
 * @author Enrico Savioli <savioli.enrico@gmail.com>
 */
public class BaseTazioNetwork implements TazioNetwork {

    private final static Logger log = LoggerFactory
	    .getLogger(BaseTazioNetwork.class);

    private final Dispatcher dispatcher;

    private final Point2PointCommunication p2pCommunication;

    private final GroupConnection groupConnection;

    private final Resolver resolver;

    private final String CONFIGURATION_FILE;

    private CSComFacade facade = null;

    /**
     * This is the default constructor for the class. It gets all the
     * dependencies it needs injected and then creates and connects a
     * {@link CSComFacade} over which it will be listening for incoming
     * messages.
     * 
     * @param dispatcher
     * @param p2pCommunication
     * @param groupConnection
     * @param CONFIGURATION_FILE
     */
    @Inject
    public BaseTazioNetwork(Dispatcher dispatcher,
	    Point2PointCommunication p2pCommunication,
	    GroupConnection groupConnection, Resolver resolver,
	    MessageFilter messageFilter, //this parameter is needed to initialize the singleton
	    @Named("facade_conf_file") String CONFIGURATION_FILE) {

	this.dispatcher = dispatcher;
	this.p2pCommunication = p2pCommunication;
	this.groupConnection = groupConnection;
	this.resolver = resolver;
	MessageFilter temp = messageFilter;
	this.CONFIGURATION_FILE = CONFIGURATION_FILE;

	connect();

    }

    private void connect() {

	try {
	    facade = new CSComFacade(Configuration
		    .fromFile(this.CONFIGURATION_FILE), ProtocolHandler.class);
	} catch (CSSubsystemException e) {
	    String msg = "Communication subsystem error while creating the server's facade";
	    log.error(msg);
	    e.printStackTrace();
	} catch (IOException e) {
	    String msg = "Input/Output error while creating the client's facade";
	    log.error(msg);
	    e.printStackTrace();
	}

	log.debug("Server's facade created.");
	facade.start();

	log.debug("RM: the facade is listening at port: " + facade.getPort());
	log.debug("RM: the facade is running " + facade.isRunning());

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transparency.interfaces.TazioNetwork#register(java
     * .lang.Class,
     * org.mentalsmash.tazio.transparency.interfaces.InputMessageListener)
     */
    @Override
    public void register(Class<? extends Serializable> msgClass,
	    InputMessageListener listener) {

	log.debug("Registering " + listener + " as a listener for " + msgClass
		+ ".");
	// System.err.println("Registering "+listener+" as a listener for "+msgClass+".");
	dispatcher.register(msgClass, listener);

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transparency.interfaces.TazioNetwork#sendToGroup
     * (java.io.Serializable)
     */
    @Override
    public <T extends Serializable> void sendToGroup(T message) {

	log.debug("Sending the message " + message + " to the group.");
	groupConnection.send(message);

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transparency.interfaces.TazioNetwork#sent(java.
     * io.Serializable, org.mentalsmash.tazio.commons.identifiers.TazioNodeID)
     */
    @Override
    public <T extends Serializable> Serializable send(T message, NID nid) {

	log.debug("Sending the message " + message + " to " + nid + ".");
	Serializable answer = p2pCommunication.send(message, nid);

	TazioMessage answerMsg = null;

	if (answer instanceof TazioMessage) {
	    answerMsg = (TazioMessage) answer;
	} else {
	    throw new RuntimeException(
		    "PORCODDIO!!! Answer received is not a TazioMessage!!!");
	}

	return answerMsg.getPayload();

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transparency.interfaces.TazioNetwork#unregister
     * (java.lang.Class,
     * org.mentalsmash.tazio.transparency.interfaces.InputMessageListener)
     */
    @Override
    public void unregister(Class<? extends Serializable> msgClass,
	    InputMessageListener listener) {

	log.debug("Unregistering " + listener + " as a listener for "
		+ msgClass + ".");
	dispatcher.unregister(msgClass, listener);

    }

    /*
     * (non-Javadoc)
     * 
     * @seeorg.mentalsmash.tazio.transparency.interfaces.TazioNetwork#
     * getWeakReplicaManagers()
     */
    @Override
    public NID[] getWeakReplicaManagers() {

	NID[] idArray = new NID[resolver.getNIDList().size()];

	List<NID> ids = this.resolver.getNIDList();

	for (int i = 0; i < ids.size(); i++) {

	    NID id = ids.get(i);
	    idArray[i] = id;

	}

	return idArray;

    }

}
